#ifndef SPARC_H
#define SPARC_H

#include "crossbit/common.h"
#include <iostream>

namespace crossbit {

    class SPARC {
        public:
            static inline void setP(XTMemAddr p)
            {
                cur_ps = (XTMemAddr *)p; 
            }

            static inline XTMemAddr getP()
            {
                return (XTMemAddr)cur_ps; 
            }

            static inline void SPARC_movImmToReg(XTInt32 imm, XTRegNum reg)
            {
                // sethi %hi(imm), %reg
                //                       
                *((XTUint32*)cur_ps++) = (reg << 25) | (4 << 22) | ((imm >> 10) & ( 0x003fffff));

                // or %reg, %lo(imm), %reg
                //                      10            rd            op3         rs1           i          %lo(imm)
                *((XTUint32*)cur_ps++) = 0x80000000 | (reg << 25) | (2 << 19) | (reg << 14) | (1 << 13) | (0x3ff & imm);
            }

            static inline void SPARC_ADD(int rd, int rs1, int i, int rs2OrSimm13)
            {
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_SUB(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                                                 op3
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (4 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_POPC(int rd, int i, int rs2OrSimm13)
            {
                //                                                 op3
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (0x2e << 19) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            
            static inline void SPARC_LDSB(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | (9 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_LDUB(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | (1 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_LDSH(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | (10 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_LDSW(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | (8 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_LDUW(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | 0  | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_STB(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | (5 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_STH(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | (6 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_STW(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                      11            rd           op3        
                *((XTUint32*)cur_ps++) = 0xc0000000 | (rd << 25) | (4 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_OR(int rd, int rs1, int i, int rs2OrSimm13)
            {
                //                                                 op3
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (2 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_AND(int rd, int rs1, int i, int rs2OrSimm13)
            {
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (1 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_ANDcc(int rd, int rs1, int i, int rs2OrSimm13)
            {
                *((XTUint32*)cur_ps++) = 0x80000000 |  (rd << 25) | (0x11 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_XOR(int rd, int rs1, int i, int rs2OrSimm13)
            {
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (3 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_NOP()
            {
                *((XTUint32*)cur_ps++) = (4 << 22);
            }

            static inline void SPARC_BA(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (8 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BE(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (1 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BNE(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (9 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BG(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (10 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BGE(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (11 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BL(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (3 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BLE(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (2 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BGU(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (12 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BCS(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (5 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_BLEU(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (4 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }
            static inline void SPARC_BCC(int disp22)
            {
                //                      cond         010
                *((XTUint32*)cur_ps++) = (13 << 25) | (2 << 22) | (disp22 & 0x3fffff); 
            }

            static inline void SPARC_RET()
            {
                // jmpl %i7+8, %g0
                *((XTUint32*)cur_ps++) = 0x81c7e008;
            }

            static inline void SPARC_SAVE(int rd, int rs1, int i, int rs2OrSimm13)
            {
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (0x3c << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_RESTORE()
            {
                // restore %g0, %g0, %g0 
                *((XTUint32*)cur_ps++) = 0x81e80000;
            }

            static inline void SPARC_MOV(int from, int to)
            {
                // or %g0, %from, %to 
                SPARC_OR(to, from, 0, 0);
            }

            static inline void SPARC_JMPL(int rd, int rs1, int i, int rs2OrSimm13)
            {
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (0x38 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }

            static inline void SPARC_CALL(int disp30)
            {
                *((XTUint32*)cur_ps++) = 0x40000000 | (disp30 & 0x3fffffff);
            }
            static inline void SPARC_SLL( int rd , int rs1 , int i , int sr2OrSimm5){
                *((XTUint32*)cur_ps++) = 0x80000000 | ( rd << 25 ) | ( 0x25 << 19 ) | ( rs1 << 14 ) | ( i << 13 ) | sr2OrSimm5;
                        }
            static inline void SPARC_SRL( int rd , int rs1 , int i , int sr2OrSimm5){
                *((XTUint32*)cur_ps++) = 0x80000000 | ( rd << 25 ) | ( 0x26 << 19 ) | ( rs1 << 14 ) | ( i << 13 ) | sr2OrSimm5;
                        }
            static inline void SPARC_SRA( int rd , int rs1 , int i , int sr2OrSimm5){
                *((XTUint32*)cur_ps++) = 0x80000000 | ( rd << 25 ) | ( 0x27 << 19 ) | ( rs1 << 14 ) | ( i << 13 ) | sr2OrSimm5;
                        }
            static inline void SPARC_SUBcc(int rd, int rs1, int i, int rs2OrSimm13)
            {
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25) | (0x14 << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_SMUL( int rd , int rs1 , int i , int rs2OrSimm13){
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25 ) | (0xb << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_UMUL( int rd , int rs1 , int i , int rs2OrSimm13){
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25 ) | (0xa << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_UDIV( int rd , int rs1 , int i , int rs2OrSimm13){
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25 ) | (0xe << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            static inline void SPARC_SDIV( int rd , int rs1 , int i , int rs2OrSimm13){
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25 ) | (0xf << 19) | (rs1 << 14) | (i << 13) | (rs2OrSimm13 & 0x1fff);
            }
            // rd %y -> rd
            static inline void SPARC_RDY( int rd ){
                *((XTUint32*)cur_ps++) = 0x80000000 | (rd << 25 ) | (0x28 << 19) ;
            }
            // wd sr1 -> %y
            static inline void SPARC_WRY( int rs1 ){
                *((XTUint32*)cur_ps++) = 0x80000000 | (0x30 << 19 ) | (rs1 << 14) ;
            }
            static inline void SWITH32( int from , int to ){
                    SPARC_movImmToReg( 0xff000000 , 22 );
                    SPARC_AND( 22 , from ,0 ,  22 );
                    SPARC_SRL( 23 , 22 , 1 , 24 );
                    SPARC_movImmToReg( 0x00ff0000 , 22 );
                    SPARC_AND( 22 , from ,0 ,  22 );
                    SPARC_SRL( 22 , 22 , 1 , 8 );
                    SPARC_OR( 23 , 22 , 0 , 23 );
                    SPARC_movImmToReg( 0x0000ff00 , 22 );
                    SPARC_AND( 22 , from , 0, 22 );
                    SPARC_SLL( 22 , 22 , 1 , 8 );
                    SPARC_OR( 23 , 22 , 0 , 23 );
                    SPARC_SLL( 22 , from , 1 , 24);
                    SPARC_OR( to , 22 , 0 , 23 );

            }
            // A : B : C : D - > A : B : D : C;
            static inline void SWITH16( int from , int to ){
                   // to = 0 : 0 : A : B
                   SPARC_SRL( to , from , 1 , 16);
                   // to = A : B : 0 : 0
                   SPARC_SLL( to , to , 1 , 16);
                   // 22 = D : 0 : 0 : 0 
                   SPARC_SLL( 22 , from , 1 , 24);
                   // 22 = 0 : 0 : D : 0
                   SPARC_SRL( 22 , 22 , 1 , 16);
                   // to = A : B : D : 0
                   SPARC_OR( to , to , 0 , 22 );
                   // 22 = C : D : 0 : 0 
                   SPARC_SLL( 22 , from , 1 , 16);
                   // 22 = 0 : 0 : 0 : C
                   SPARC_SRL( 22 , 22 , 1 , 24);
                   // to = A : B : D : C
                   SPARC_OR( to , to , 0 , 22 );
            }
        private:
            static XTMemAddr* cur_ps;
    };

}

#endif
